using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.Engine;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Metadata.Entity;

namespace T.FAS.Runtime.DataAccess.Service
{
    /// <summary>
    /// 统一数据访问服务
    /// </summary>
    internal class UnifiedDataAccessService : IUnifiedDataAccessService
    {
        private readonly IDACLogService s_localLogService = DACLogFactory.GetDACLogService();

        //线程同步对象
        private static object syncObj = new object();

        //数据访问服务统一入口
        private static IUnifiedDataAccessService instance;

        public IDataAccessEngine DataAccessEngine { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        private UnifiedDataAccessService()
        {

        }

        /// <summary>
        /// 获取数据访问服务统一入口
        /// </summary>
        /// <returns>数据访问服务统一入口</returns>
        public static IUnifiedDataAccessService GetInstance()
        {
            if (instance == null)
            {
                lock (syncObj)
                {
                    if (instance == null)
                    {
                        instance = new UnifiedDataAccessService();
                    }
                }
            }

            return instance;
        }

        #region Save

        /// <summary>
        /// 保存CDO数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="data">通用结果对象</param>
        public TResult SaveDataForCDO(CompositeDataObject compositeDataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_SaveDataForCDOFail_DataCannotBeNull,compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOSaveService().SaveData(compositeDataObject, data, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Create.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_SaveDataForCDOFail_WithMsg,compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 保存指定对象的数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="compositeNodeObjectID">组合数据对象节点ID</param>
        /// <param name="data">通用结果对象</param>
        public TResult SaveDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, TRecord data, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (string.IsNullOrWhiteSpace(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_SaveDataForCDO2,compositeDataObject?.Code));
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_SaveDataForCDOFail_DataCannotBeNull, compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOSaveService().SaveData(compositeDataObject, compositeNodeObjectID, data, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Create.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_SaveDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 保存数据对象数据
        /// </summary>
        /// <param name="dataObject">数据对象</param>
        /// <param name="data">通用结果对象</param>
        public TResult SaveDataForDataObject(DataObject dataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(dataObject);
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_SaveDataForDataObject,dataObject?.Code));

            try
            {
                return DataAccessEngine.CreateDOSaveService().SaveData(dataObject, data, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Create.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_SaveDataForDataObject1,dataObject?.Code,e.Message), e);
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// 更新CDO数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="data">通用结果对象</param>
        public TResult UpdateDataForCDO(CompositeDataObject compositeDataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForCDOFail_DataCannotBeNull,compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOUpdateService().UpdateData(compositeDataObject, data, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Update.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForCDOFail_WithMsg,compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 更新CDO数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="datas">通用结果对象</param>
        public BatchUpdateResult BatchUpdateDataForCDO(CompositeDataObject compositeDataObject, List<TRecord> datas, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (datas == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_BatchUpdateDataForCDO,compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOUpdateService().BatchUpdateData(compositeDataObject, datas, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Update.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 更新CDO数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="compositeNodeObjectID">组合数据对象节点ID</param>
        /// <param name="data">通用结果对象</param>
        public TResult UpdateDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, TRecord data, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForCDOFail_DataCannotBeNull, compositeDataObject?.Code));
            if (string.IsNullOrEmpty(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForCDO3,compositeDataObject?.Code));


            try
            {
                return DataAccessEngine.CreateCDOUpdateService().UpdateData(compositeDataObject, compositeNodeObjectID, data, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Update.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 更新数据对象数据
        /// </summary>
        /// <param name="dataObject">数据对象</param>
        /// <param name="data">通用结果对象</param>
        public TResult UpdateDataForDataObject(DataObject dataObject, TRecord data, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(dataObject);
            if (data == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForDataObject,dataObject?.Code));

            try
            {
                return DataAccessEngine.CreateDOUpdateService().UpdateData(dataObject, data, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Update.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_UpdateDataForDataObject1,dataObject?.Code,e.Message), e);
            }
        }

        #endregion

        #region Delete

        /// <summary>
        ///  删除CDO数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="dataId">数据唯一标识</param>        
        public void DeleteDataForCDO(CompositeDataObject compositeDataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (string.IsNullOrEmpty(dataId))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDO,compositeDataObject?.Code));

            try
            {
                DataAccessEngine.CreateCDODeleteService().DeleteByID(compositeDataObject, dataId, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Delete.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDOFail_WithMsg,compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        ///  批量删除CDO数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="dataIds">数据唯一标识集合</param>        
        public void DeleteDataSetForCDO(CompositeDataObject compositeDataObject, IList<string> dataIds, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (dataIds == null || dataIds.Count == 0)
                return;

            try
            {
                DataAccessEngine.CreateCDODeleteService().DeleteByIDList(compositeDataObject, dataIds, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Delete.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        ///  删除CDO节点数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="compositeNodeObjectID">CDO节点ID</param>
        /// <param name="dataId">数据唯一标识</param>        
        public void DeleteDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, string dataId, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (string.IsNullOrEmpty(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDOFail_CNOIdCannotBeNull,compositeDataObject?.Code));
            if (string.IsNullOrEmpty(dataId))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDO3,compositeDataObject?.Code));

            try
            {
                DataAccessEngine.CreateCDODeleteService().DeleteByID(compositeDataObject, compositeNodeObjectID, dataId, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Delete.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        ///  删除数据
        /// </summary>
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="compositeNodeObjectID">CDO节点ID</param>
        /// <param name="dataIds">数据唯一标识集合</param>        
        public void DeleteDataSetForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, IList<string> dataIds, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (string.IsNullOrEmpty(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDOFail_CNOIdCannotBeNull, compositeDataObject?.Code));
            if (dataIds == null || dataIds.Count == 0)
                return;

            try
            {
                DataAccessEngine.CreateCDODeleteService().DeleteByIDList(compositeDataObject, compositeNodeObjectID, dataIds, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Delete.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        ///  删除数据对象数据
        /// </summary>
        /// <param name="dataObject">数据对象</param>
        /// <param name="dataId">数据唯一标识</param>        
        public void DeleteDataForDataObject(DataObject dataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(dataObject);
            if (string.IsNullOrEmpty(dataId))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForDataObject,dataObject?.Code));

            try
            {
                DataAccessEngine.CreateDODeleteService().DeleteByID(dataObject, dataId, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Delete.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForDOFail_CNOIdCannotBeNull,dataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        ///  批量删除数据对象数据
        /// </summary>
        /// <param name="dataObject">数据对象</param>
        /// <param name="dataIds">数据唯一标识集合</param>        
        public void DeleteDataSetForDataObject(DataObject dataObject, IList<string> dataIds, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(dataObject);
            if (dataIds == null || dataIds.Count == 0)
                return;

            try
            {
                DataAccessEngine.CreateDODeleteService().DeleteByIDList(dataObject, dataIds, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Delete.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_DeleteDataForDOFail_CNOIdCannotBeNull, dataObject?.Code,e.Message), e);
            }
        }

        #endregion

        #region Query

        /// <summary>
        /// 获取指定CDO的数据
        /// </summary>        
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="dataId">数据唯一标识</param>        
        /// <returns>通用结果数据</returns>
        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (string.IsNullOrEmpty(dataId))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_DataIdCannotBeNull,compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOQueryService().QueryData(compositeDataObject, dataId, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Retrieve.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_WithMsg,compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 获取指定CDO的数据
        /// </summary>        
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="compositeNodeObjectID">CDO节点ID</param>
        /// <param name="dataId">数据唯一标识</param>        
        /// <returns>通用结果数据</returns>
        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, string dataId, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (string.IsNullOrEmpty(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_CNOIdCannotBeNull,compositeDataObject?.Code));
            if (string.IsNullOrEmpty(dataId))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_DataIdCannotBeNull, compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOQueryService().QueryData(compositeDataObject, compositeNodeObjectID, dataId, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Retrieve.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 获取指定CDO的数据
        /// </summary>        
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="queryFilter">查询过滤器</param>              
        /// <returns>通用结果数据</returns>
        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, QueryFilter queryFilter, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (queryFilter == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_FilterCannotBeNull,compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOQueryService().QueryData(compositeDataObject, queryFilter, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Retrieve.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 获取指定CDO的数据
        /// </summary>        
        /// <param name="compositeDataObject">CDO</param>
        /// <param name="compositeNodeObjectID">CDO节点ID</param>
        /// <param name="queryFilter">查询过滤器</param>        
        /// <returns>通用结果数据</returns>
        public TResult QueryDataForCDO(CompositeDataObject compositeDataObject, string compositeNodeObjectID, QueryFilter queryFilter, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(compositeDataObject);
            if (queryFilter == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_FilterCannotBeNull, compositeDataObject?.Code));
            if (string.IsNullOrEmpty(compositeNodeObjectID))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_CNOIdCannotBeNull, compositeDataObject?.Code));

            try
            {
                return DataAccessEngine.CreateCDOQueryService().QueryData(compositeDataObject, compositeNodeObjectID, queryFilter, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Retrieve.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForCDOFail_WithMsg, compositeDataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 获取指定数据对象的数据
        /// </summary>        
        /// <param name="dataObject">数据对象</param>
        /// <param name="dataId">数据唯一标识</param>        
        /// <returns>通用结果数据</returns>
        public TResult QueryDataForDataObject(DataObject dataObject, string dataId, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(dataObject);
            if (string.IsNullOrEmpty(dataId))
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForDataObject,dataObject?.Code));

            try
            {
                return DataAccessEngine.CreateDOQueryService().QueryData(dataObject, dataId, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Retrieve.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForDataObject1,dataObject?.Code,e.Message), e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataObject"></param>
        /// <param name="parameters"></param>
        /// <param name="exrParams"></param>
        /// <returns></returns>
        /// <exception cref="FasException"></exception>
        public TResult QueryDataForDataObject(DataObject dataObject, Dictionary<string, object> parameters, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(dataObject); 

            try
            {
                var dq = VirtualModelService.GenerateVirtualDQ(dataObject);
                return QueryDataForDataQuery(dq, parameters, exrParams);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Retrieve.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForDataObject1, dataObject?.Code, e.Message), e);
            }
        }

        /// <summary>
        /// 执行DataQuery数据查询
        /// </summary>
        /// <param name="dataQuery">DataQuery</param>
        /// <returns>通用结果数据</returns>
        public TResult QueryDataForDataQuery(DataQuery dataQuery, Dictionary<string, object> parameters, Dictionary<string, object> exrParams = null)
        {
            ValidateMetaData(dataQuery);

            try
            {
                return DataAccessEngine.CreateDataQueryService().Query(dataQuery, parameters);
            }
            catch (Exception e)
            {
                var extParams = new ExtLogInfo { MetaDataCode = dataQuery.MetaDataCode };
                s_localLogService.WriteError(e, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForDataQuery, dataQuery?.Code, e.Message), extParams);
                throw new Exception(string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForDataQuery, dataQuery?.Code, e.Message) + $"\r\nerrStack:{e}");
                //throw new FasException(DataAccessErrorCode.Retrieve.Error, string.Format(CommonStringClassService.UnifiedDataAccessService_QueryDataForDataQuery, dataQuery?.Code, e.Message), e);
            }
        }

        #endregion

        public DataSet Query(FasDBCommand fasDBCommand)
        {
            return DataAccessEngine.CreateSQLService().Query(fasDBCommand);
        }

        public string GetDQActionSQL(DataQuery dataQuery)
        {
            var service = DataAccessEngine.CreateDataQueryService();
            var sql = service.GetQueryString(dataQuery);
            return sql;
        }

        #region Private Funcs

        public TRecord RefreshTRecord(CompositeDataObject cdo, TRecord record)
        {
            throw new NotImplementedException();
        }

        public bool IsHaveStateMachine(CompositeDataObject cdo)
        {
            ValidateMetaData(cdo);
            var statemachines = cdo.CompositeNodeObjects.FirstOrDefault(node => node.IsRootObject).Elements.SelectMany(ele =>
            {
                if (ele.StateMachines != null && ele.StateMachines.Count > 0)
                {
                    return ele.StateMachines;
                }
                else
                {
                    return null;
                }
            }).Where(s => s != null).ToList();
            return statemachines.Count > 0;
        }

        private void ValidateMetaData(IMetaDataContent metaDataContent)
        { 
            if (metaDataContent == null)
                throw new FasException(DataAccessErrorCode.NullValue, string.Format(CommonStringClassService.UnifiedDataAccessService_ValidateMetaData));
        }

        #endregion
    }
}
